#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
视频监控系统 - 服务端 (修改版)
功能：接收采集端发送的视频流，提供Web界面供用户查看，支持控制采集端
"""

import asyncio
import websockets
import json
import base64
from flask import Flask, render_template_string, jsonify, request
import threading
import time
from datetime import datetime
import logging

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

class VideoServer:
    def __init__(self, websocket_port=8765, web_port=5000):
        self.websocket_port = websocket_port
        self.web_port = web_port
        self.connected_clients = set()  # WebSocket客户端
        self.connected_cameras = {}  # 摄像头采集端 {websocket: camera_info}
        self.latest_frame = None
        self.frame_timestamp = None
        self.current_camera = None  # 当前显示的摄像头
        self.camera_status = {}  # 摄像头状态 {camera_id: {'capturing': True/False}}
        self.app = Flask(__name__)
        self.setup_web_routes()
        
    def setup_web_routes(self):
        """设置Web路由"""
        @self.app.route('/')
        def index():
            return render_template_string(WEB_TEMPLATE)
        
        @self.app.route('/api/frame')
        def get_latest_frame():
            """获取最新帧数据"""
            if self.latest_frame and self.frame_timestamp:
                # 检查帧是否太旧（超过5秒）
                if time.time() - self.frame_timestamp < 5:
                    return jsonify({
                        'success': True,
                        'frame': self.latest_frame,
                        'timestamp': self.frame_timestamp,
                        'camera_id': self.current_camera
                    })
            
            return jsonify({
                'success': False,
                'message': 'No recent frame available'
            })
        
        @self.app.route('/api/status')
        def get_status():
            """获取系统状态"""
            return jsonify({
                'cameras_connected': len(self.connected_cameras),
                'clients_connected': len(self.connected_clients),
                'last_frame_time': self.frame_timestamp,
                'current_camera': self.current_camera,
                'camera_status': self.camera_status
            })
        
        @self.app.route('/api/control', methods=['POST'])
        def control_camera():
            """控制摄像头"""
            try:
                data = request.get_json()
                command = data.get('command')
                camera_id = data.get('camera_id', self.current_camera)
                
                if not camera_id:
                    return jsonify({'success': False, 'message': 'No camera selected'})
                
                # 发送控制命令到对应摄像头
                success = asyncio.run(self.send_control_command(camera_id, command))
                
                if success:
                    # 更新状态
                    if camera_id not in self.camera_status:
                        self.camera_status[camera_id] = {}
                    
                    if command == 'start_capture':
                        self.camera_status[camera_id]['capturing'] = True
                    elif command == 'stop_capture':
                        self.camera_status[camera_id]['capturing'] = False
                    
                    return jsonify({'success': True, 'message': f'Command {command} sent successfully'})
                else:
                    return jsonify({'success': False, 'message': 'Failed to send command'})
                    
            except Exception as e:
                logger.error(f"Control error: {e}")
                return jsonify({'success': False, 'message': str(e)})
        
        @self.app.route('/api/cameras')
        def get_cameras():
            """获取摄像头列表"""
            cameras = []
            for ws, info in self.connected_cameras.items():
                cameras.append({
                    'camera_id': info['camera_id'],
                    'connected_time': info.get('connected_time', ''),
                    'capturing': self.camera_status.get(info['camera_id'], {}).get('capturing', False)
                })
            return jsonify({'cameras': cameras})

    async def send_control_command(self, camera_id, command):
        """发送控制命令到指定摄像头"""
        target_camera = None
        for ws, info in self.connected_cameras.items():
            if info['camera_id'] == camera_id:
                target_camera = ws
                break
        
        if not target_camera:
            logger.warning(f"Camera {camera_id} not found")
            return False
        
        try:
            control_message = json.dumps({
                'type': 'control_command',
                'command': command,
                'timestamp': time.time()
            })
            await target_camera.send(control_message)
            logger.info(f"Sent command '{command}' to camera {camera_id}")
            return True
        except Exception as e:
            logger.error(f"Failed to send control command: {e}")
            return False

    async def handle_websocket(self, websocket):
        """处理WebSocket连接"""
        try:
            remote_addr = f"{websocket.remote_address[0]}:{websocket.remote_address[1]}"
            logger.info(f"New WebSocket connection from {remote_addr}")
            
            async for message in websocket:
                try:
                    data = json.loads(message)
                    
                    if data.get('type') == 'camera_register':
                        # 摄像头注册
                        camera_id = data.get('camera_id', 'unknown')
                        self.connected_cameras[websocket] = {
                            'camera_id': camera_id,
                            'connected_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                        }
                        
                        # 如果是第一个摄像头，设为当前显示
                        if not self.current_camera:
                            self.current_camera = camera_id
                        
                        # 初始化状态 - 修改：默认为停止采集
                        if camera_id not in self.camera_status:
                            self.camera_status[camera_id] = {'capturing': False}
                        
                        logger.info(f"Camera registered: {camera_id}")
                        await websocket.send(json.dumps({
                            'type': 'register_response',
                            'success': True,
                            'message': 'Camera registered successfully'
                        }))
                        
                    elif data.get('type') == 'frame_data':
                        # 接收视频帧 - 只处理当前选中的摄像头
                        camera_id = data.get('camera_id')
                        if camera_id == self.current_camera:
                            self.latest_frame = data.get('frame')
                            self.frame_timestamp = time.time()
                            
                            # 转发给所有连接的客户端
                            await self.broadcast_frame(data)
                        
                    elif data.get('type') == 'client_register':
                        # 客户端注册
                        self.connected_clients.add(websocket)
                        logger.info("Web client registered")
                        await websocket.send(json.dumps({
                            'type': 'register_response',
                            'success': True,
                            'message': 'Client registered successfully'
                        }))
                    
                    elif data.get('type') == 'control_response':
                        # 控制命令响应
                        logger.info(f"Control response: {data.get('message', 'No message')}")
                        
                except json.JSONDecodeError:
                    logger.error("Invalid JSON received")
                except Exception as e:
                    logger.error(f"Error processing message: {e}")
                    
        except websockets.exceptions.ConnectionClosed:
            logger.info("WebSocket connection closed")
        except Exception as e:
            logger.error(f"WebSocket error: {e}")
        finally:
            # 清理连接
            if websocket in self.connected_cameras:
                camera_info = self.connected_cameras[websocket]
                logger.info(f"Camera disconnected: {camera_info['camera_id']}")
                del self.connected_cameras[websocket]
                
                # 如果断开的是当前摄像头，切换到下一个
                if camera_info['camera_id'] == self.current_camera:
                    if self.connected_cameras:
                        self.current_camera = list(self.connected_cameras.values())[0]['camera_id']
                    else:
                        self.current_camera = None
            
            self.connected_clients.discard(websocket)

    async def broadcast_frame(self, frame_data):
        """向所有客户端广播视频帧"""
        if self.connected_clients:
            message = json.dumps({
                'type': 'video_frame',
                'frame': frame_data.get('frame'),
                'timestamp': frame_data.get('timestamp', time.time()),
                'camera_id': frame_data.get('camera_id')
            })
            
            disconnected = set()
            for client in self.connected_clients:
                try:
                    await client.send(message)
                except websockets.exceptions.ConnectionClosed:
                    disconnected.add(client)
                except Exception as e:
                    logger.error(f"Error sending frame to client: {e}")
                    disconnected.add(client)
            
            self.connected_clients -= disconnected

    def run_websocket_server(self):
        """运行WebSocket服务器"""
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        
        async def server_main():
            server = await websockets.serve(
                self.handle_websocket,
                "0.0.0.0",
                self.websocket_port
            )
            logger.info(f"WebSocket server started on port {self.websocket_port}")
            await server.wait_closed()
        
        try:
            loop.run_until_complete(server_main())
        except Exception as e:
            logger.error(f"WebSocket server error: {e}")
        finally:
            loop.close()

    def run_web_server(self):
        """运行Web服务器"""
        logger.info(f"Web server starting on port {self.web_port}")
        self.app.run(host='0.0.0.0', port=self.web_port, debug=False)

    def start(self):
        """启动服务器"""
        websocket_thread = threading.Thread(target=self.run_websocket_server, daemon=True)
        websocket_thread.start()
        time.sleep(1)
        self.run_web_server()

# 修改后的Web页面模板
WEB_TEMPLATE = '''
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>视频监控系统</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            padding: 20px;
        }
        
        .container {
            max-width: 1200px;
            margin: 0 auto;
            background: white;
            border-radius: 15px;
            box-shadow: 0 20px 40px rgba(0,0,0,0.1);
            overflow: hidden;
        }
        
        .header {
            background: linear-gradient(45deg, #2c3e50, #34495e);
            color: white;
            padding: 20px 30px;
            text-align: center;
        }
        
        .header h1 {
            font-size: 2.5rem;
            margin-bottom: 10px;
            text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
        }
        
        .status-bar {
            background: #ecf0f1;
            padding: 15px 30px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            border-bottom: 1px solid #bdc3c7;
            flex-wrap: wrap;
            gap: 10px;
        }
        
        .status-item {
            display: flex;
            align-items: center;
            gap: 10px;
        }
        
        .status-dot {
            width: 12px;
            height: 12px;
            border-radius: 50%;
            background: #e74c3c;
            animation: pulse 2s infinite;
        }
        
        .status-dot.connected {
            background: #27ae60;
        }
        
        @keyframes pulse {
            0% { opacity: 1; }
            50% { opacity: 0.5; }
            100% { opacity: 1; }
        }
        
        .control-panel {
            background: #f8f9fa;
            padding: 20px 30px;
            border-bottom: 1px solid #dee2e6;
            display: flex;
            justify-content: center;
            gap: 15px;
            flex-wrap: wrap;
        }
        
        .video-container {
            padding: 30px;
            text-align: center;
            display: flex;
            flex-direction: column;
            align-items: center;
        }
        
        .video-wrapper {
            position: relative;
            display: inline-block;
            max-width: 100%;
        }
        
        #videoCanvas {
            max-width: 100%;
            height: auto;
            border: 3px solid #34495e;
            border-radius: 10px;
            box-shadow: 0 10px 30px rgba(0,0,0,0.2);
            background: #2c3e50;
            display: block;
        }
        
        .no-signal {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            color: #ecf0f1;
            font-size: 1.2rem;
            text-align: center;
            pointer-events: none;
        }
        
        .loading {
            width: 50px;
            height: 50px;
            border: 5px solid rgba(236, 240, 241, 0.3);
            border-top: 5px solid #ecf0f1;
            border-radius: 50%;
            animation: spin 1s linear infinite;
            margin-bottom: 20px;
        }
        
        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }
        
        .controls {
            margin-top: 20px;
            display: flex;
            justify-content: center;
            gap: 15px;
            flex-wrap: wrap;
        }
        
        .btn {
            padding: 12px 24px;
            border: none;
            border-radius: 25px;
            cursor: pointer;
            font-size: 16px;
            font-weight: 600;
            transition: all 0.3s ease;
            text-transform: uppercase;
            letter-spacing: 1px;
        }
        
        .btn-primary {
            background: linear-gradient(45deg, #3498db, #2980b9);
            color: white;
        }
        
        .btn-primary:hover {
            transform: translateY(-2px);
            box-shadow: 0 5px 15px rgba(52, 152, 219, 0.4);
        }
        
        .btn-secondary {
            background: linear-gradient(45deg, #95a5a6, #7f8c8d);
            color: white;
        }
        
        .btn-secondary:hover {
            transform: translateY(-2px);
            box-shadow: 0 5px 15px rgba(149, 165, 166, 0.4);
        }
        
        .btn-success {
            background: linear-gradient(45deg, #27ae60, #2ecc71);
            color: white;
        }
        
        .btn-success:hover {
            transform: translateY(-2px);
            box-shadow: 0 5px 15px rgba(46, 204, 113, 0.4);
        }
        
        .btn-danger {
            background: linear-gradient(45deg, #e74c3c, #c0392b);
            color: white;
        }
        
        .btn-danger:hover {
            transform: translateY(-2px);
            box-shadow: 0 5px 15px rgba(231, 76, 60, 0.4);
        }
        
        .btn:disabled {
            opacity: 0.6;
            cursor: not-allowed;
            transform: none !important;
            box-shadow: none !important;
        }
        
        .info-panel {
            background: #f8f9fa;
            margin: 20px 30px;
            padding: 20px;
            border-radius: 10px;
            border-left: 4px solid #3498db;
        }
        
        .camera-info {
            background: #e8f4fd;
            padding: 10px 15px;
            border-radius: 8px;
            margin-bottom: 15px;
            border-left: 4px solid #3498db;
        }
        
        @media (max-width: 768px) {
            .header h1 {
                font-size: 2rem;
            }
            
            .status-bar {
                flex-direction: column;
                gap: 10px;
            }
            
            .video-container {
                padding: 20px;
            }
            
            .control-panel {
                justify-content: center;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>🎥 视频监控系统</h1>
            <p>实时视频监控平台 - 单摄像头显示</p>
        </div>
        
        <div class="status-bar">
            <div class="status-item">
                <div class="status-dot" id="connectionStatus"></div>
                <span id="connectionText">连接状态: 断开</span>
            </div>
            <div class="status-item">
                <span>📹 摄像头: <strong id="cameraCount">0</strong></span>
            </div>
            <div class="status-item">
                <span>👥 在线用户: <strong id="clientCount">0</strong></span>
            </div>
            <div class="status-item">
                <span id="lastUpdate">最后更新: --</span>
            </div>
        </div>
        
        <div class="control-panel">
            <button class="btn btn-success" id="startBtn" onclick="controlCamera('start_capture')">
                ▶️ 开始采集
            </button>
            <button class="btn btn-danger" id="stopBtn" onclick="controlCamera('stop_capture')">
                ⏹️ 停止采集
            </button>
            <button class="btn btn-primary" onclick="reconnect()">🔄 重新连接</button>
        </div>
        
        <div class="video-container">
            <div class="camera-info" id="cameraInfo" style="display: none;">
                <strong>当前摄像头:</strong> <span id="currentCamera">--</span> | 
                <strong>采集状态:</strong> <span id="captureStatus">--</span>
            </div>
            
            <div class="video-wrapper">
                <canvas id="videoCanvas" width="640" height="480"></canvas>
                <div class="no-signal" id="noSignal">
                    <div class="loading"></div>
                    <p>等待视频信号...</p>
                    <small>请确保采集端已连接并正常工作</small>
                </div>
            </div>
            
            <div class="controls">
                <button class="btn btn-secondary" onclick="toggleFullscreen()">🖥️ 全屏</button>
                <button class="btn btn-secondary" onclick="captureFrame()">📸 截图</button>
            </div>
        </div>
        
        <div class="info-panel">
            <h3>📊 系统信息</h3>
            <p><strong>服务器地址:</strong> <span id="serverInfo">正在获取...</span></p>
            <p><strong>连接类型:</strong> WebSocket 实时连接</p>
            <p><strong>视频格式:</strong> MJPEG 流</p>
            <p><strong>显示模式:</strong> 单摄像头模式</p>
        </div>
    </div>

    <script>
        let ws = null;
        let canvas = null;
        let ctx = null;
        let isConnected = false;
        let currentCameraId = null;
        let cameraStatus = {};
        
        // 初始化
        document.addEventListener('DOMContentLoaded', function() {
            canvas = document.getElementById('videoCanvas');
            ctx = canvas.getContext('2d');
            document.getElementById('serverInfo').textContent = window.location.host;
            
            connectWebSocket();
            updateStatus();
            
            // 定期更新状态
            setInterval(updateStatus, 3000);
        });
        
        function connectWebSocket() {
            const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
            const wsUrl = `${protocol}//${window.location.hostname}:8765`;
            
            try {
                ws = new WebSocket(wsUrl);
                
                ws.onopen = function() {
                    console.log('WebSocket连接已建立');
                    isConnected = true;
                    updateConnectionStatus(true);
                    
                    // 注册为客户端
                    ws.send(JSON.stringify({
                        type: 'client_register'
                    }));
                };
                
                ws.onmessage = function(event) {
                    try {
                        const data = JSON.parse(event.data);
                        
                        if (data.type === 'video_frame') {
                            displayFrame(data.frame);
                            currentCameraId = data.camera_id;
                            updateCameraInfo();
                            document.getElementById('lastUpdate').textContent = 
                                '最后更新: ' + new Date().toLocaleTimeString();
                        }
                    } catch (e) {
                        console.error('处理消息错误:', e);
                    }
                };
                
                ws.onclose = function() {
                    console.log('WebSocket连接已关闭');
                    isConnected = false;
                    updateConnectionStatus(false);
                    
                    // 5秒后尝试重连
                    setTimeout(connectWebSocket, 5000);
                };
                
                ws.onerror = function(error) {
                    console.error('WebSocket错误:', error);
                    isConnected = false;
                    updateConnectionStatus(false);
                };
                
            } catch (e) {
                console.error('WebSocket连接失败:', e);
                setTimeout(connectWebSocket, 5000);
            }
        }
        
        function displayFrame(frameData) {
            if (!frameData) return;
            
            const img = new Image();
            img.onload = function() {
                // 隐藏无信号提示
                document.getElementById('noSignal').style.display = 'none';
                
                // 调整canvas尺寸
                canvas.width = img.width;
                canvas.height = img.height;
                
                // 绘制图像
                ctx.drawImage(img, 0, 0);
            };
            img.src = 'data:image/jpeg;base64,' + frameData;
        }
        
        function updateConnectionStatus(connected) {
            const statusDot = document.getElementById('connectionStatus');
            const statusText = document.getElementById('connectionText');
            
            if (connected) {
                statusDot.classList.add('connected');
                statusText.textContent = '连接状态: 已连接';
            } else {
                statusDot.classList.remove('connected');
                statusText.textContent = '连接状态: 断开';
                
                // 显示无信号提示
                document.getElementById('noSignal').style.display = 'flex';
                document.getElementById('cameraInfo').style.display = 'none';
            }
        }
        
        async function updateStatus() {
            try {
                const response = await fetch('/api/status');
                const data = await response.json();
                
                document.getElementById('cameraCount').textContent = data.cameras_connected;
                document.getElementById('clientCount').textContent = data.clients_connected;
                
                currentCameraId = data.current_camera;
                cameraStatus = data.camera_status || {};
                
                updateCameraInfo();
            } catch (e) {
                console.error('更新状态失败:', e);
            }
        }
        
        function updateCameraInfo() {
            const cameraInfo = document.getElementById('cameraInfo');
            const currentCameraSpan = document.getElementById('currentCamera');
            const captureStatusSpan = document.getElementById('captureStatus');
            
            if (currentCameraId) {
                cameraInfo.style.display = 'block';
                currentCameraSpan.textContent = currentCameraId;
                
                const isCapturing = cameraStatus[currentCameraId]?.capturing || false;
                captureStatusSpan.textContent = isCapturing ? '采集中' : '已停止';
                captureStatusSpan.style.color = isCapturing ? '#27ae60' : '#e74c3c';
                
                // 更新按钮状态
                document.getElementById('startBtn').disabled = isCapturing;
                document.getElementById('stopBtn').disabled = !isCapturing;
            } else {
                cameraInfo.style.display = 'none';
                document.getElementById('startBtn').disabled = true;
                document.getElementById('stopBtn').disabled = true;
            }
        }
        
        async function controlCamera(command) {
            if (!currentCameraId) {
                alert('没有可用的摄像头');
                return;
            }
            
            try {
                const response = await fetch('/api/control', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({
                        command: command,
                        camera_id: currentCameraId
                    })
                });
                
                const result = await response.json();
                if (result.success) {
                    console.log('控制命令发送成功:', command);
                    // 立即更新状态
                    setTimeout(updateStatus, 500);
                } else {
                    alert('控制命令发送失败: ' + result.message);
                }
            } catch (e) {
                console.error('发送控制命令失败:', e);
                alert('发送控制命令失败');
            }
        }
        
        function reconnect() {
            if (ws) {
                ws.close();
            }
            connectWebSocket();
        }
        
        function toggleFullscreen() {
            if (!document.fullscreenElement) {
                canvas.requestFullscreen().catch(err => {
                    console.error('无法进入全屏模式:', err);
                });
            } else {
                document.exitFullscreen();
            }
        }
        
        function captureFrame() {
            const link = document.createElement('a');
            link.download = 'capture_' + new Date().toISOString().slice(0,19).replace(/:/g, '-') + '.png';
            link.href = canvas.toDataURL();
            link.click();
        }
    </script>
</body>
</html>
'''

if __name__ == "__main__":
    server = VideoServer()
    logger.info("Starting Video Monitoring Server...")
    logger.info("Web interface: http://localhost:5000")
    logger.info("WebSocket port: 8765")
    server.start()